#!/bin/perl -w

# author: yang.jin@ebay.com 

use strict;

use Env;
use File::Basename;
use File::Copy;
use File::Rsync;
use File::stat;
use Mail::Sender;
use Net::DNS;
use Net::SCP qw(scp);
use Rcs;
use Text::Diff;
use Time::localtime; 


use constant REMOTEFILE => 'itadmin@10.244.15.33:/home/itadmin/bl/blacklisted_domains';

use constant DOMAIN_MAXLENGTH	=> 256;
use constant DOMAIN_MINNUMBER => 500;
use constant DOMAIN_MAXNUMBER => 50000;

use constant DBTEMPLATE => '
$ttl 38400
BLACKLISTED_DOMAIN.     IN	SOA	denitdns01.vip.its.ebay.com. dl-ebay-get-ops-unix.corp.ebay.com. (
			SERIAL_NO
			10800
			3600
			604800
			38400 )

BLACKLISTED_DOMAIN.	IN	NS	denitdns01.vip.its.ebay.com.
BLACKLISTED_DOMAIN.	IN	NS	denitdns02.vip.its.ebay.com.

*.BLACKLISTED_DOMAIN.	IN	A	IP_REDIRECTED
BLACKLISTED_DOMAIN.	IN	A	IP_REDIRECTED
';

# Commands 
use constant CHECKCONF	=> '/opt/bind/sbin/named-checkconf';
use constant DIG 	=> "/opt/bind/bin/dig";

# ================================================================
#  :.,+9 s/^/# /	:.,+9 s/^# //
#
# ####################### for Production #################################
use constant CONFIG 	=> '/etc/named/bl.zones';
use constant DBPATH 	=> '/var/named/db/bl';
use constant LOGFILE	=> '/ebay/bind-logs/named_update_bl.log';
use constant TMPDIR	=> '/var/named/tmp';
use constant TMPFILE	=> '/var/named/tmp/bl.list';
use constant LOCKFILE	=> '/var/named/tmp/named_update_bl.LOCK';
use constant LOCALFILE	=> '/etc/named/bl.list.DONOT-REMOVE';
use constant SERVERCFG	=> '/etc/named/IT_DNS_SERVERS';
use constant GISMAIL	=> 'DL-eBay-IS-SecurityMonitoring@corp.ebay.com';
use constant UNIXMAIL	=> 'DL-eBay-GET-Ops-Unix@ebay.com';
use constant RNDCRELOAD	=> '/opt/bind/sbin/rndc reload';
# ================================================================
# ####################### for Testing #################################
# use constant CONFIG 	=> '/TEST/etc/named/bl.zones';
# use constant DBPATH 	=> '/TEST/var/named/db/bl';
# use constant LOGFILE	=> '/TEST/ebay/bind-logs/named_update_bl.log';
# use constant TMPDIR	=> '/TEST/var/named/tmp';
# use constant TMPFILE	=> '/TEST/var/named/tmp/bl.list';
# use constant LOCALFILE	=> '/TEST/etc/named/bl.list.DONOT-REMOVE';
# use constant SERVERCFG => '/TEST/etc/named/IT_DNS_SERVERS';
# use constant GISMAIL	=> 'yajin@ebay.com';
# use constant UNIXMAIL	=> 'DL-eBay-GET-Ops-Unix@ebay.com';
# use constant RNDCRELOAD	=> '# /opt/bind/sbin/rndc reload';




####################################################################################
#########################	Begin of MAIN		############################ 
####################################################################################

my $dbfile;
my @origcfglines; # read in readconfig();

my $prg = basename($0);

my $pid = $$;


# =============================================================================
# SCP the list from a remote host, and compare the old one in local
# exit if no differences, .....................
# scp REMOTEFILE to TMPFILE, and then compare TMPFILE against LOCAL

my $logfile = LOGFILE;
open(LOG,">>$logfile") || die "failed to open file \"$logfile\", error - $!\n";

# Checking a job is already running ...
my $lockfile = LOCKFILE;
if ( -f $lockfile ) {
	writelog("a previous job is still running. exiting...");
	writelog("NOTE: remove lock file $lockfile and rerun the job if not running");
	exit 1;
}
else {
	open(LOCK, ">$lockfile") or udie("failed to open lock file \"$lockfile\"");
	printf LOCK "PID: %s\n", $pid;
	close(LOCK);
	writelog("lock file \"$lockfile\" created"); 
}



my $remotefile	= REMOTEFILE;
my $tmpfile	= TMPFILE;
my $localfile	= LOCALFILE;

my  $scp = new Net::SCP;
$scp->scp($remotefile, $tmpfile) or udie("failed to scp $remotefile to $tmpfile, error - $scp->{errstr}");
writelog("succeeded to scp $remotefile to $tmpfile");

my $diff = diff $localfile, $tmpfile, { STYLE => "OldStyle" };

if ( "X$diff" eq "X" ) {
	writelog("no differences found between \"$localfile\" and \"$tmpfile\"");
	removelock();
	exit 0;
} 
else {
	writelog("differences found between \"$localfile\" and \"$tmpfile\":\n$diff");
}

# =============================================================================
# get the pairs of domain and destination IP addres and sort them 
# format in @cfglist & @srclist: domain destination-IP-address
# e.g., 
# shv4b.getmyip.com 10.244.15.33

my $cfgfile = CONFIG; 

writelog("start to read configuration $cfgfile and get IP addresses of domains by reading their DB files");
my @cfglist = readconfig($cfgfile);
writelog("$#cfglist domains are read from configuration $cfgfile and sorted");

my $errorstr = ""; #  global, it is assigned in readsource() if errors

writelog("start to read source $tmpfile");
my @srclist = readsource($tmpfile);
writelog("$#srclist domains are read from source $tmpfile and sorted");

# =============================================================================
# verify if the number of domains less than the minimum number
my $minno = DOMAIN_MINNUMBER;
if ( $#srclist < DOMAIN_MINNUMBER ) {
	writelog("error: number of domains($#srclist) is less than the minimum number($minno)"); 
	$errorstr = $errorstr . "\n\nnumber of domains is less than the minimum number($minno)"; 

	my $listremotefile = `/bin/ssh itadmin\@10.244.15.33 "ls -al /home/itadmin/bl/blacklisted_domains" `;
	my $listlocalfile = `ls -al $tmpfile` ;

	$errorstr = $errorstr . "\n\nremote file:\n" . $listremotefile . "\nlocal file:\n" . $listlocalfile;

# save the file as an evidence, is it needed to do: ssh host "ls -al xxx"???
###	my $tm = localtime();
###	my $backupfile = sprintf("%s.%04d%02d%02d%02d%02d%02d", $tmpfile, $tm->year+1900, $tm->mon+1, $tm->mday, $tm->hour, $tm->min, $tm->sec);
###
###	copy("$tmpfile", "$backupfile") or udie("failed to copy $tmpfile to $backupfile, error - $!");
###	writelog("\"$tmpfile\" copied to \"$backupfile\"");

}

 # =============================================================================
# verify if the number of domains greater than the maximum number
my $maxno = DOMAIN_MAXNUMBER;
if ( $#srclist > DOMAIN_MAXNUMBER ) {
	writelog("error: number of domains is greater than the maximum number($maxno)"); 
	$errorstr = $errorstr . "\n\nnumber of domains is greater than the maximum number($maxno)"; 
}

# =============================================================================
# verify domain uniqueness
my $srcdn =""; 
my @srclist2 = @srclist;
foreach (@srclist2) {
	chomp;
	$_ =~ s/\s.*//;
	if ( "X$srcdn" eq "X$_" ) {
		writelog("error: duplicate domain: $srcdn");
		$errorstr = $errorstr . "\n\nduplicate domain: $srcdn";
	}
	$srcdn = $_;
}

# =============================================================================
# send the notification to infosec and abort if any invalid domain, 
# any invalid IP address, any duplicate domain, or, less than minimum domains
if ( "X$errorstr" ne "X" ) { 

	$errorstr = "Please fix the following errors with $remotefile as soon as possible:\n" . $errorstr ; 

	my $sender = new Mail::Sender { smtp => 'localhost'};

 	$sender->MailMsg({
		from => UNIXMAIL,
		subject => "Errors with the Remote List for Black-Listing",
		to => GISMAIL,
		msg => $errorstr
	});

	$sender->Close;
	
	my $receiver = GISMAIL; 	
	writelog("error messages sent to $receiver");
	writelog("exiting with errors");

	removelock(); 
	exit 1;	
}

# =============================================================================
# compare the 2 domain lists from configuration and the remote host.

$diff = diff \@cfglist, \@srclist, { STYLE => "OldStyle" };

if ( "X$diff" eq "X" ) {  
	writelog("no changes to domain/IP pairs according to configuration and remote list");
	removelock();
	exit 0; 
}
# =============================================================================

my @difflines = split(/\n/, $diff);

writelog("differences between configuration and remote list: \n$diff");

my @toberemoved = ();
my @tobeadded = ();

# get domains to be removed, or, to be added.
foreach (@difflines) {
	if ( /^>\s*/ ) {
		chomp;
		$_ =~ s/^>\s*//;
		push(@tobeadded, $_); 
		next;
	}

	if ( /^<\s*/ ) {
		chomp;
		$_ =~ s/^<\s*//;
		push(@toberemoved, $_); 
		next;
	}
}

# foreach (@toberemoved) { writelog("domain/IP pair \"$_\" is to be removed"); }
# foreach (@tobeadded) { writelog("domain/IP pair \"$_\" is to be added"); }

my $origcfg = join("", @origcfglines);
my $tmptext = $origcfg;

# ----------------------------------------------------------------------
# removing domains in the configuration and removing their DB files

foreach (@toberemoved) {
	$_ =~ s/ .*//;
	my $tmp = $_;
	$tmptext =~  s/\s*zone\s+\"$tmp\"\s*\{\s+type\s+master\s*\;\s*file\s+\".*\"\s*\;\s*\}\s*\;//; 
	$tmptext =~  s/\s*zone\s+\"$tmp\"\s*\{\s+check\-names\s+warn\;\s*type\s+master\s*\;\s*file\s+\".*\"\s*\;\s*\}\s*\;//; 

	### removing the zone DB file!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	### too early to remove????????????????????????, it could be just the time!!!
	$dbfile = DBPATH . "/db." . $tmp;
	unlink($dbfile);
	writelog("domain \"$tmp\" removed and db \"$dbfile\" purged");
}


# ----------------------------------------------------------------------
# add domains in the configuration and create their DB files

# generate the serial number of a DB file, serial=YYYYMMDD$((HH*4 + mm*10/75))
my $tm = localtime();
my $serial = sprintf("%04d%02d%02d%02d", $tm->year+1900, $tm->mon+1, $tm->mday, $tm->hour*4+$tm->min*10/75);

foreach (@tobeadded){
	chomp;
	my ($dn, $ip) = split;
	$tmptext = "$tmptext\nzone \"$dn\" \{\n\tcheck-names warn\;\n\ttype master\;\n\tfile \"bl\/db.$dn\"\;\n\}\;\n";
	# creating the zone DB file!!!
	my $dbcontent = DBTEMPLATE;
	$dbcontent =~ s/IP_REDIRECTED/$ip/g;
	$dbcontent =~ s/BLACKLISTED_DOMAIN/$dn/g;
	$dbcontent =~ s/SERIAL_NO/$serial/;

	$dbfile = DBPATH . "/db." . $dn;

	open(OUT,">$dbfile") || udie("failed to create file \"$dbfile\", error - $!\n");
	printf OUT "%s\n", $dbcontent;
	close OUT;
	writelog("domain \"$dn\" added and db \"$dbfile\" created");

}

# ----------------------------------------------------------------------
# create the new configuration file

my $newconfig = basename(CONFIG);
$newconfig = TMPDIR . "/" . $newconfig;

open(OUT, ">$newconfig") || udie("failed to create file \"$newconfig\", error - $!\n");
printf OUT "%s", $tmptext;
close OUT;
writelog("new configuration written to $newconfig");

# ----------------------------------------------------------------------
# validate the new configuration file with named-checkconf

my $mycmd = CHECKCONF;
$errorstr = `$mycmd $newconfig`;

if ( "X$errorstr" ne "X" ) { 

	writelog("error: failed to pass configuration verification - \"$newconfig\" -\n$errorstr");

	$errorstr = "Error: failed to pass configuration verification - \"$newconfig\"" . $errorstr; 

	my $receiver = UNIXMAIL; 	
	my $sender = new Mail::Sender { smtp => 'localhost'};

 	$sender->MailMsg({
		from => UNIXMAIL,
		subject => "[unixmon]: DNS(named_update_bl) Errors with an Interim Configuration File for Black-Listing",
		to => $receiver,
		msg => $errorstr
	});

	$sender->Close;
	
	writelog("error messages sent to $receiver");
	writelog("exiting with errors");

	removelock();
	exit 2;	
}
else { 
	writelog("verify configuration file \"$newconfig\" - passed"); 
}

# ----------------------------------------------------------------------
# check in the new configuration file with RCS

my $confdir = dirname(CONFIG);
my $conffile = CONFIG;

my $finfo = stat($conffile);
my $mode = $finfo->mode;
$mode &= 0200;
if ( $mode == 0200 ) { udie("error: failed to check out to writable $conffile"); }

my $ok = chdir $confdir;
# exit if the file checked out
if ( $ok != 1 ) { udie("failed to \"cd $confdir\", error - $!"); }

$conffile = basename($conffile);
Rcs->quiet(1);      # turn on quiet mode
my $obj = Rcs->new;
$obj->file("$conffile");

$obj->co('-l'); # check out and lock file, die if failure
writelog("RCS - \"$conffile\" checked out");

copy("$newconfig", "$conffile") or udie("failed to copy $newconfig to $confdir/$conffile, error - $!");
writelog("\"$newconfig\" copied to \"$confdir/$conffile\"");

# my $rcsdiff = $obj->rcsdiff; 

# unlock and check in 
$obj->ci('-u', "-mauto checked in by $prg.");
writelog("RCS - \"$conffile\" checked in");

# ----------------------------------------------------------------------
# overwrite LOCALFILE with TMPFILE

copy($tmpfile, $localfile) or udie("failed to copy $tmpfile to $localfile, error - $!");
writelog("succeeded to copy \"$tmpfile\" to \"$localfile\"");


# ----------------------------------------------------------------------
# reload named on the local host, not needed on sjcitdns01

### $mycmd = RNDCRELOAD;
### my $ret = `$mycmd`;
### if ( $ret eq "server reload successful\n" ) { 
### 	writelog("succeeded to reload named"); 
### }
### else {
### 	writelog("failed to reload named, error - $!"); 
### 	exit 1;
### }

# ----------------------------------------------------------------------
# distribute changes
# copy the configuration file - bl.zones to other DNS servers
# distribute DB files /var/named/db/bl by "rsync"

my $DNS_PRIMARY_MASTER;
my $DNS_PSEUDO_MASTERS;
my $DNS_SLAVES;

readservercfg(SERVERCFG);

my @dnsmasters = split(/\s+/, $DNS_PSEUDO_MASTERS);
my @dnsslaves = split(/\s+/, $DNS_SLAVES);

# only those DNS servers for AD users 
my @dnsservers = ("phxitdns03.vip.its.ebay.com", "phxitdns04.vip.its.ebay.com", "sjnitdns01.vip.its.ebay.com", "sjnitdns02.vip.its.ebay.com", "sjcitdns03.sjc.its.ebay.com", "sjcitdns04.sjc.its.ebay.com", "denitdns03.vip.its.ebay.com","denitdns04.vip.its.ebay.com", "dubitdns01.vip.its.ebay.com", "dubitdns02.vip.its.ebay.com");


### foreach (@dnsmasters, @dnsslaves) {
foreach (@dnsservers) {

	my $host = $_;

	my $robj = File::Rsync->new( { 
			archive => 1, 
			compress => 1, 
			rsh => '/bin/ssh', 
			'rsync-path' => '/usr/local/bin/rsync'
	} );

	my $dest = dirname(CONFIG); 
	$dest = $host . ":" . $dest;
	
	$robj->exec( {  
		src => CONFIG,
		dest => $dest
	} ) or udie("failed to rsync configuration to $host, error - $!");

	writelog("succeeded to rsync configuration to $host");

	$dest = dirname(DBPATH); 
	$dest = $host . ":" . $dest;

	$robj->exec( {
			src => DBPATH, 
			dest => $dest
	} ) or udie("failed to rsync DBs to $host, error - $!");

	writelog("succeeded to rsync DBs to $host");

	# reload named
	$mycmd = RNDCRELOAD;
	my $ret = `/bin/ssh -q $host "$mycmd"`;
	if ( $ret eq "server reload successful\n" ) { 
		writelog("succeeded to reload named on $host"); 
	}
	else {
		writelog("failed to reload named on $host, error -\n $ret"); 
	# exit 1;
	}

}

removelock(); 
writelog("$prg completed");
close(LOG);

##################################################################
# information? send a notification for the success, needed?

####################################################################################
#########################	End of MAIN		############################ 
####################################################################################

# =======================================================================================
sub readconfig {

my ($cfgfile) = @_;
my $zone;
my $ip;

my @zonelist = ();
my $zonestatement= "";
my @lines = fgetlines($cfgfile);

@origcfglines = @lines; # assign to a global variable

    foreach (@lines) {
	chomp;
	$_ =~ s/(\/\/|#).*//;   # remove the notes
	$_ =~ s/\/\*.*\*\///;	# remove the notes
	$_ =~ s/\s+/ /g; # suppress spaces

	# remove options
 	$_ =~ s/ *allow\-transfer *\{(.*)\} *\;//;
 	$_ =~ s/ *allow\-update *\{(.*)\} *\;//;
 	$_ =~ s/ *allow\-query *\{(.*)\} *\;//;
	$_ =~ s/ *also notify [\d\.\w]+ *\;//;
	$_ =~ s/ *notify [\d\.\w]+ *\;//;
	$_ =~ s/ *check-names [\d\.\w]+ *\;//;

	$_ =~ s/\" in \{/\" \{/i;  # remove "in" following the zone name

	# remove blank lines
	if ( /^\s*$/ ) { next; }  
	
	if ( /^\s*zone\s/ ) {
 		if ( $zonestatement ) { 
			writelog("\nerror: Zone statement cannot be analayzed: $zonestatement"); 
			$zonestatement = $_; 
		}
		else { 
			$zonestatement = "$zonestatement" . "$_";
		}
	} 
	else {
		if ( $zonestatement ) { $zonestatement = "$zonestatement" . "$_"; }
		else  { next; }
	}
	
	$_ = $zonestatement; 

	if ( / *zone \"(.*)\" \{ *type master *\; *file \"(.*)\" *\; *\} *\;/ ) {
		$zone = $1;

#  		my $res   = Net::DNS::Resolver->new;
#		my $query = $res->query("$zone", "A");
#  		if ($query) {
#			my $rr;
#      			foreach $rr (grep { $_->type eq 'A' } $query->answer) {
#          			# print $rr->address, "\n";
#				my $ip = $rr->address; 
#				printf STDOUT "%-80s\t%-16s\n", $zone, $ip;
#      			}
#  		}
#  		else {
#			warn "query failed: ", $res->errorstring, "\n";
#  		}
#
####		my $mycmd = DIG;
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
####		$ip = `$mycmd \@10.112.155.235 $zone +norecurse +short | egrep -v "^;|^$"`; 
####		chomp $ip;
####		# verify $ip before use !!!!!!!!!!!!!!!!!!!!!!

		($ip) = getip($zone); 

		if ( "X$ip" eq "X" ) { writelog("warning: domain configured cannot be resolved(format not matched in DB file): $zone"); }

		my $record = "$zone $ip\n";
		push(@zonelist, $record);

		$zonestatement = "";
	}
	else { next; }
    } # end of foreach (@lines)

    # my @sorted = sort { $a->[0] cmp $b->[0] } @zonelist; # 
    my @sorted = sort @zonelist;
    return @sorted;

} # end of read_config 

# =======================================================================================
sub getip {

my ($domain) = @_;

my $myfile = "/var/named/db/bl/db." . "$domain";

my @lines = fgetlines($myfile);

foreach (@lines) {
        chomp;
        my $line = $_;
        if ( /^$domain\.\sIN\s+A\s+10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) {
                $_ =~ s/.*\s+//;
		return($_);
        }
}
return("");

}


# =======================================================================================
sub readsource {
	
my ($srcfile) = @_;
my $zone;
my $ip;

my @zonelist = ();
my @lines = fgetlines($srcfile);

    foreach (@lines) {
	chomp;
	my $line = $_;
	$_ =~ s/(\/\/|#).*//;   # remove the notes, only "#" and "//" allowed for comments in the source
	$_ =~ tr/A-Z/a-z/; # convert to lower cases
	if ( /^\s*$/ ) { next; }  # skip the blank lines/comment lines

	if ( /^\s*([\w\.\-]+)\s+([\d\.]+)\s*$/ ) { 
		$zone = $1;	
		$ip = $2;

		######################################
		### VERIFY DOMAIN
		my $rval = verifydomain($zone);
		if ( $rval != 0 ) {
			$errorstr = $errorstr . "\ninvalid domain name: $line";
			writelog("error: invalid domain name: $line");
			# $errorno++;
			next;
		}

		######################################
		### VERIFY IP ADDRESS
		$rval = verifyip($ip);
		if ( $rval != 0 ) {
			$errorstr = $errorstr . "\ninvalid IP address: $line";
			writelog("error: invalid IP address: $line");
			# $errorno++;
			next;
		}

		my @ipset = split(/\./, $ip);

		# remove leading "0" in an IP address, possible?  
		$ip = "";
		foreach (@ipset) {
			if ( "X$ip" ne "X" ) { $ip = sprintf("%s.",$ip); }
			$ip = sprintf("%s%d", $ip, $_);
		}

		my $record = "$zone $ip\n";
		push(@zonelist, $record);
	}
	else {
		$errorstr = $errorstr . "\ninvalid syntax or charactors: $line";
		writelog("error: invalid syntax or charactors: $line");
		# $errorno++;
	}


    } # end of foreach (@lines)

    my @sorted = sort @zonelist;
    return @sorted;

} # end of readsource

# =======================================================================================
sub fgetlines {
	my ($file) = @_;
	my @data = ();

	open(FILE,"< $file") || udie("failed to open file \"$file\", error - $!");
	@data = <FILE>;
	close(FILE);
	return @data;
}

# -----------------------------------------------------------------------------------------
sub verifydomain {
	my ($domain) = @_;

	# domains not meeting the format of ([\w\.\-]+) have been skipped in "readsource"
	# verify the length of the a domain

	$_ = $domain;

	my $i = length;
	my $len = DOMAIN_MAXLENGTH;
	if ( $i >= DOMAIN_MAXLENGTH ){ 
		$errorstr = $errorstr . "\ndomain length exceeds the maximum length($len): $domain";
		writelog("error: domain length exceeds the maximum length($len): $domain");
		return 999; 
	}

	if ( /\.$/  || /^\./ ) { return 10; }
	if ( /\.\./ ) { return 11; }

	my @subdoms = split(/\./, $domain);
	
	$i = 0;

	foreach (@subdoms){ 
		my $rval = verifysubdomain($_); # ????
		if ( $rval != 0 ) { 
			writelog("subdomain: $_; errorcode: $rval");
			return $rval; 
		}
		$i++;
	}

	if ( $i <= 1 ) { 
		$errorstr = $errorstr . "\nroot domain not allowed: $domain";
		writelog("error: root domain not allowed: $domain");
		return 99; 
	} # root domain not allowed!

	return 0; 
}

# -----------------------------------------------------------------------------------------
sub verifysubdomain {
	my ($subdn) = @_;

	$_ = $subdn;
	# not needed? # if ( /^\w+$/ ) { return 0; } # only letters and/or digits
	if ( /^[a-zA-Z0-9'-_]+$/ ) {
		if ( /^\-/ || /\-$/ ) { return 2; } # "-" not allowd in the beginning or tailing of a subdomain
		# if ( /\-\-/ ) {return 3; } 
	}
	else { return 1; }

	return 0;
}

# -----------------------------------------------------------------------------------------
sub verifyip {
	my ($ip) = @_;

	$_ = $ip;
	if ( /^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) {
		$_ =~ s/^10\.//;
		my @subip = split(/\./);
		
		my $tval;
		foreach $tval (@subip) {
			if ( ( $tval >= 0 ) && ( $tval <= 255 ) ) { next; }
			else { return 2; }
		}
	}
	else { return 1; }

	return 0;
}
# -----------------------------------------------------------------------------------------
sub writelog {
	my ($msg) = @_;
	my $tm = localtime();
	my $stime = sprintf("%04d %02d %02d %02d:%02d:%02d", $tm->year+1900, $tm->mon+1, $tm->mday, $tm->hour, $tm->min, $tm->sec);
	printf LOG "%s PID(%s): %s\n", $stime, $pid, $msg;
}

# -----------------------------------------------------------------------------------------
sub readservercfg {

	my ($servercfg) = @_;
	my @lines = fgetlines($servercfg);
	my $line;
	my $statement = "";
	
	foreach (@lines) {
		chomp;
		if (/^\s*$/) { next; }
		$line = $_;
		$line =~ s/\n/ /;
		$statement = $statement . $line;
		$_ = $statement;

		if (/DNS_PRIMARY_MASTER\s*=\s*\"(.*)\"/) {
			$DNS_PRIMARY_MASTER = $1;
			$statement = "";
		}
		elsif (/DNS_PSEUDO_MASTERS\s*=\s*\"(.*)\"/) {
			$DNS_PSEUDO_MASTERS = $1;
			$statement = "";
		}
		elsif (/DNS_SLAVES\s*=\s*\"(.*)\"/) {
			$DNS_SLAVES= $1;
			$statement = "";
		}
	}

	$DNS_PRIMARY_MASTER =~ s/^\s*//;
	$DNS_PRIMARY_MASTER =~ s/\s*$//;

	$DNS_PSEUDO_MASTERS =~ s/^\s*//;
	$DNS_PSEUDO_MASTERS =~ s/\s*$//;

	$DNS_SLAVES =~ s/^\s*//;
	$DNS_SLAVES =~ s/\s*$//;
}
# -----------------------------------------------------------------------------------------

sub udie {
	my ($msg) = @_;
	writelog($msg); 
	removelock();
	close(LOG);
	die "$msg\n";
}

sub removelock {
  if ( unlink($lockfile) ) {
	writelog("lock file \"$lockfile\" removed");
  }
  else {
	writelog("failed to remove \"$lockfile\"");
  }
}


# ===================================
# PATH of Perl Modules Installation
# ===================================
# Digest-SHA-PurePerl-5.50  
# Digest-HMAC-1.02          
# Net-IP-1.25  
# => Net-DNS-0.66
# 
# Net-SSH-0.09
# String-ShellQuote-1.04
# => Net-SCP-0.08 
# 
# Algorithm-Diff-1.1902
# => Text-Diff-1.37
#              
# Rcs-1.05
# Mail-Sender-0.8.16  
# File-Rsync-0.43 
